#include "notifymanager.h"
#include "common/defines.h"

#include "converse/converseabstractmessage.h"
#include "converse/conversetextmessage.h"
#include "converse/conversephotomessage.h"
#include "converse/conversevoicemessage.h"
#include "converse/conversemanager.h"

#include "notifymessagenotification.h"
#include "notifytextmessagenotification.h"
#include "notifyonlinestatusnotification.h"

#include "appchat/accountmanager.h"
#include "account/accountbuddy.h"
#include "account/accountuser.h"

#include <QTimer>
#include <QDebug>


namespace Notify {

Manager *Manager::m_instance = NULL;

Manager::Manager(QObject *parent) :
    QObject(parent)
{
    setConnection();
}


Manager *Manager::instance()
{
    if (!m_instance)
        m_instance = new Manager();
    return m_instance;
}


void Manager::setConnection() const
{
    connect(Converse::Manager::instance(),
            &Converse::Manager::messageReceived,
            this,
            &Manager::onMessageReceived);

//    connect(AccountManager::instance(),
//            &AccountManager::onlineStatusChanged,
//            this,
//            &Manager::onOnlineStatusChanged);
}


void Manager::onMessageReceived(Converse::AbstractMessage *message)
{
    if (message == NULL)
        return;

    int userId = AccountManager::instance()->getUserId();

    int senderId = message->senderId();
    if (senderId == userId)
        return;

    if (message->type() == Converse::AbstractMessage::TextType) {
        QTimer::singleShot(10*1000, this, SLOT(onNotificationTimeout()));
        Converse::TextMessage *textMessage = dynamic_cast<Converse::TextMessage *>(message);
        Q_ASSERT(textMessage);
        TextMessageNotification *notification = createTextMessageNotification(textMessage);
        Q_ASSERT(notification);
        append(notification);
    }
}


void Manager::onOnlineStatusChanged(Account::Buddy *buddy)
{
    if (buddy == NULL)
        return;

    QTimer::singleShot(10*1000, this, SLOT(onNotificationTimeout()));
    OnlineStatusNotification *statusNotification = createStatusNotification(buddy);
    Q_ASSERT(statusNotification);
    append(statusNotification);
}


void Manager::onNotificationTimeout()
{
    if (m_notificationList.isEmpty())
        return;

    Notification *first = m_notificationList.first();
    removeFirstItem();
    first->deleteLater();
}


void Manager::itemAboutToBeAppended(Notification *notification)
{
    if (notification == NULL)
        return;

    emit notificationAboutToBeAppended(notification);
}


void Manager::itemAppended(Notification *notification)
{
    if (notification == NULL)
        return;

    emit notificationAppended(notification);
}


void Manager::itemAboutToBeRemoved(Notification *notification)
{
    if (notification == NULL)
        return;

    emit notificationAboutToBeRemoved(notification);
}


void Manager::itemRemoved(Notification *notification)
{
    if (notification == NULL)
        return;

    emit notificationRemoved(notification);
}


void Manager::append(Notification *notification)
{
    QMutexLocker locker(&m_mutex);
    if (notification == NULL)
        return;

    if (contains(notification))
        return;

    itemAboutToBeAppended(notification);
    m_notificationList.append(notification);
    itemAppended(notification);
}


void Manager::remove(Notification *notification)
{
    QMutexLocker locker(&m_mutex);
    if (notification == NULL)
        return;

    if (!contains(notification))
        return;

    itemAboutToBeRemoved(notification);
    m_notificationList.removeAt(indexOf(notification));
    itemRemoved(notification);
    notification->deleteLater();

    if (m_notificationList.isEmpty())
        emit dataEmpty();
}


void Manager::remove(int index)
{
    QMutexLocker locker(&m_mutex);
    if (m_notificationList.isEmpty())
        return;

    if ((index < 0) || (index >= count()))
        return;

    itemAboutToBeRemoved(this->at(index));
    m_notificationList.removeAt(index);
    itemRemoved(this->at(index));

    if (m_notificationList.isEmpty())
        emit dataEmpty();
}


void Manager::removeFirstItem()
{
    QMutexLocker locker(&m_mutex);
    if (m_notificationList.isEmpty())
        return;

    Notification *notification = this->at(0);
    if (notification == NULL)
        return;

    itemAboutToBeRemoved(notification);
    m_notificationList.removeAt(0);
    itemRemoved(notification);

    if (m_notificationList.isEmpty())
        emit dataEmpty();
}


TextMessageNotification *Manager::createTextMessageNotification(Converse::TextMessage *message)
{
    return new TextMessageNotification(message);
}


OnlineStatusNotification *Manager::createStatusNotification(Account::Buddy *buddy)
{
    return new OnlineStatusNotification(buddy);
}


bool Manager::contains(Notification *notification) const
{
    if (m_notificationList.isEmpty())
        return false;

    if (!m_notificationList.contains(notification))
        return false;

    return true;
}


int Manager::count() const
{
    return m_notificationList.count();
}


int Manager::size() const
{
    return m_notificationList.size();
}


Notification *Manager::at(int index) const
{
    if ((index < 0) || (index >= count()))
        return NULL;

    return m_notificationList.at(index);
}


int Manager::indexOf(Notification *notification) const
{
    if (notification == NULL)
        return -1;

    return m_notificationList.indexOf(notification);
}


void Manager::clearData()
{
    qDeleteAll(m_notificationList);
    m_notificationList.clear();
    emit dataCleared();
}


void Manager::onCloseItem(Notification *notification)
{
    if (!contains(notification))
        return;

    remove(notification);
}


void Manager::onNotificationWidgetAboutToBeClosed()
{
    clearData();
}

} // namespace Notify
